import { format } from 'prettier'
import postcss from 'prettier/plugins/postcss'
import typescript from 'prettier/plugins/typescript'
import estree from 'prettier/plugins/estree'
import tinycolor from 'tinycolor2'
import { camel, dash, pascal, snake } from 'radash'
import { logError } from './utils'

export interface ColorGroup {
  [Hue: number]: string[]
}

export interface ColorAlias {
  [K: string]: string
}

/**
 * 变量类型
 */
export enum ColorVartype {
  LESS = 'less',
  SCSS = 'scss',
  CSS_VAR = 'css',
  TYPESCRIPT = 'typescript'
}

export const colorPrefix: Record<ColorVartype, string> = {
  [ColorVartype.LESS]: '@',
  [ColorVartype.SCSS]: '$',
  [ColorVartype.CSS_VAR]: '--',
  [ColorVartype.TYPESCRIPT]: ''
}

export const colorPostfix: Record<ColorVartype, string> = {
  [ColorVartype.LESS]: '.less',
  [ColorVartype.SCSS]: '.scss',
  [ColorVartype.CSS_VAR]: '.css',
  [ColorVartype.TYPESCRIPT]: '.ts'
}

export enum ColorAliasJoint {
  /** ColorAliasJoint */
  UpperCamelCase = 'a',
  /** colorAliasJoint */
  LowerCamelCase = 'b',
  /** color_alias_joint */
  Underline = 'c',
  /** color-alias-joint */
  TransverseLine = 'd'
}

export const colorAliasJoint: Record<ColorAliasJoint, (str: string) => string> = {
  [ColorAliasJoint.UpperCamelCase]: pascal,
  [ColorAliasJoint.LowerCamelCase]: camel,
  [ColorAliasJoint.Underline]: snake,
  [ColorAliasJoint.TransverseLine]: dash
}

export interface ColorObject {
  /** 项目名称 */
  key: string
  /** 已确认的颜色 */
  confirmed: ColorGroup
  /** 未确认的颜色 */
  unconfirmed: string[]
  /** 颜色别名 */
  alias: ColorAlias
  /** 前缀 */
  space?: string
  /** 变量类型 */
  vartype: ColorVartype
  /** 导出 js 变量 */
  jsvar?: boolean
  /** 别名的连接规则 */
  joint: ColorAliasJoint
  /** 颜色汲取器选择的颜色直接添加到 confirmed 中 */
  eyeConfirmed?: boolean
  /** 颜色选择器选择的颜色直接添加到 confirmed 中 */
  pickerConfirmed?: boolean
  /** 实时匹配粘贴板中的颜色值 */
  realTimeMatching?: boolean
}

export interface StoreState {
  color: ColorObject[]
}

export enum ColorVerifyResult {
  /** 包含在 confirmed 中 */
  Confirmed,
  /** 包含在 unconfirmed 中 */
  Unconfirmed,
  /** 有效但不包含在任意类型中 */
  None
}

export function toHex(color: tinycolor.Instance) {
  return color.getAlpha() === 1 ? color.toHexString() : color.toHex8String()
}

export function getHue(color: tinycolor.Instance) {
  const { h, s } = color.toHsl()
  return !h && !s ? -1 : Math.round(h)
}

export function getColorAlias(object: ColorObject | undefined, value: string) {
  if (!object) return

  const color = tinycolor(value)

  if (!color.isValid()) return

  const { alias } = object

  // 获取
  const alpha = color.getAlpha()
  if (alpha === 1) return alias[color.toHexString()]

  let name = alias[color.toHex8String()]

  if (!name) {
    name = alias[color.toHexString()]
    if (name) name = `${name}_x${Math.round(alpha * 100)}`
  }

  return name
}

export function setColorAlias(object: ColorObject | undefined, value: string, name?: string) {
  if (!object) return

  const color = tinycolor(value)

  if (!color.isValid()) return

  const { alias } = object

  const hex = toHex(color)
  if (name) {
    alias[hex] = name
  } else {
    delete alias[hex]
  }
}

export function colorSpaceAlias(object: ColorObject | undefined, value: string, transform = false) {
  if (!object) return
  let name = getColorAlias(object, value)
  if (!name) return
  const { space, joint } = object
  if (space) {
    name = `${space}${name}`
  }
  return transform ? colorAliasJoint[joint](name) : name
}

const colorTextWrappage: Record<ColorVartype, (list: [name: string, value: string][]) => string> = {
  [ColorVartype.LESS](list) {
    const prefix = colorPrefix[ColorVartype.LESS]
    return list.map(([name, value]) => `${prefix}${name}: ${value};`).join('\n')
  },
  [ColorVartype.SCSS](list) {
    const prefix = colorPrefix[ColorVartype.SCSS]
    return list.map(([name, value]) => `${prefix}${name}: ${value};`).join('\n')
  },
  [ColorVartype.CSS_VAR](list) {
    const prefix = colorPrefix[ColorVartype.CSS_VAR]
    const text = list.map(([name, value]) => `${prefix}${name}: ${value};`).join('\n')
    return `:root,:host {\n${text}\n}`
  },
  [ColorVartype.TYPESCRIPT](list) {
    const prefix = colorPrefix[ColorVartype.TYPESCRIPT]
    const text = list.map(([name, value]) => `'${prefix}${name}': '${value}',`).join('\n')
    return `const data = {\n${text}\n}\n\nexport default data`
  }
}

export function colorObject2Text(object?: ColorObject) {
  if (!object) return Promise.reject('请先选择项目')

  const { confirmed, vartype, jsvar } = object

  const list: [name: string, value: string][] = []

  const values = Object.values(confirmed).flat()
  for (let index = values.length; index--; ) {
    const value = values[index]
    const name = colorSpaceAlias(object, value, true)

    if (!name) {
      return Promise.reject('请先补全颜色别名')
    }

    list.push([name, value])
  }

  if (!list.length) return Promise.reject('请先记录颜色信息')

  const varText = colorTextWrappage[vartype](list)

  const jsValueFormat: (name: string) => string =
    vartype === ColorVartype.CSS_VAR ? (name) => `${name}: var(${colorPrefix[vartype]}${name})` : (name) => `${name}: ${colorPrefix[vartype]}${name}`

  const jsText = jsvar && vartype !== ColorVartype.TYPESCRIPT ? `:export {\n ${list.map(([name]) => jsValueFormat(name)).join(';\n')} \n}` : ''

  const text = `${varText}\n\n${jsText}`

  return format(text, {
    tabWidth: 2,
    endOfLine: 'lf',
    trailingComma: 'all',
    parser: vartype,
    plugins: [postcss, typescript, estree]
  }).catch((error) => {
    logError(error)
    return text
  })
}

export const colorRegExp = /(\#([0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})|((rgba?|hsla?)\([0-9,./\s]+?\)))/gi

export const variateRegExp = /(--|@|\$)([a-zA-Z0-9-_]+):\0{0,}(.+);/gi
